import com.googlecode.lightest.studio.Annotation
import com.googlecode.lightest.studio.Test

/**
 * Supports copying of Test domain objects. A "copy" transfers metadata
 * information describing a Test, while preserving certain specific attributes,
 * such as the Test's name and className, as well as test history and suite
 * membership.
 */
class TestCopierService {

    boolean transactional = true

    /**
     * For each of tests, copies the test to an existing domain object if it
     * exists, or creates and saves a new domain object if it doesn't. Returns
     * a Collection of tests that could not be copied due to validation errors.
     *
     * @param tests
     */
    def copyAll(Collection<Test> tests) {
        def notCopied = []

        for (test in tests) {
            if (! test.validate()) {
                notCopied << test
                continue
            }

            def existingTest = Test.findByNameAndClassName(test.name,
                test.className)

            if (existingTest != null) {
                copy(test, existingTest)
            }
            else {
                test.save()
            }
        }

        return notCopied

    }

    /**
     * Copies from to dest. The name and className attributes are ignored in
     * this copy operation. Any Annotation's on from are copied, and any
     * Annotation's present only on dest are removed. However, neither
     * testSuites nor testResults are modified in any way.
     *
     * @param from  the test to copy information from
     * @param dest  the test to copy information to
     */
    def copy(Test from, Test dest) {
        dest.description = from.description

        for (a in from.annotations) {
            def destA = dest.annotations.find { it == a }
            if (! destA) {
                destA = new Annotation(name: a.name, member: a.member)
                copy(a, destA)
                dest.addToAnnotations(destA)
            }
            else {
                copy(a, destA)
            }
        }

        notIn(dest.annotations, from.annotations).each {
            dest.removeFromAnnotations(it)
            it.delete()
        }
    }

    /**
     * Copies from to dest.
     *
     * @param from
     * @param dest
     */
    def copy(Annotation from, Annotation dest) {
        for (value in from.values) {
            if (! dest.values.find { it == value }) {
                dest.addToValues(value)
            }
        }

        notIn(dest.values, from.values).each {
            dest.removeFromValues(it)
            it.delete()
        }
    }

    /**
     * Returns a Set of objects that are in collectionA, but NOT in
     * collectionB.
     *
     * @param collectionA
     * @param collectionB
     */
    private Set notIn(collectionA, collectionB) {
        def notIn = new HashSet()
        
        for (a in collectionA) {
            if (! collectionB.find { it == a }) {
                notIn << a
            }
        }

        return notIn
    }
}
